Skip to content

simplexml: filter addChild() result by the created element's namespace - #23599

Open
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:promote/simplexml-addchild-ns
Open

simplexml: filter addChild() result by the created element's namespace#23599
iliaal wants to merge 1 commit into
php:PHP-8.4from
iliaal:promote/simplexml-addchild-ns

Conversation

@iliaal

@iliaal iliaal commented Sep 6, 2026

Copy link
Copy Markdown
Member

SimpleXMLElement::addChild() built the returned element with the prefix the caller passed in rather than the one the new node ended up in, so a child added through that returned object was filtered against the wrong namespace and could not be reached by property name. Taking the prefix from newnode->ns fixes all three cases: an explicit a:kid with a matching uri, a bare kid with a uri, and a bare kid under a prefixed parent where xmlNewChild inherits the parent's prefix.

The filter keys on the prefix rather than the href deliberately. node_as_zval() installs no filter at all when the prefix is null or empty, so switching to href matching would start filtering the default-namespace case that currently has none.

@iliaal
iliaal requested a review from devnexen as a code owner September 6, 2026 15:07
@iliaal
iliaal force-pushed the promote/simplexml-addchild-ns branch from 0a411a4 to 10af2d1 Compare September 6, 2026 15:08
iliaal added a commit to iliaal/php-src that referenced this pull request Sep 6, 2026
addChild() built the returned SimpleXMLElement with the prefix the caller
passed in, not the one the new node ended up in, so a child added through
that object was filtered against the wrong namespace and was invisible by
property name. Take the prefix from newnode->ns instead. The filter keys
on the prefix rather than the href because node_as_zval() installs no
filter at all for a prefixless namespace, and switching to href would
start filtering the default-namespace case that today has none.

Closes phpGH-23599
Comment thread ext/simplexml/simplexml.c Outdated
}

node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname, prefix, 0);
node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only filter when the caller asked for a namespace: fixes the two broken forms without changing what addChild('kid') under a prefixed parent can read, which would otherwise be a BC break on a stable branch.

diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -1679,6 +1679,7 @@ PHP_METHOD(SimpleXMLElement, addChild)
      xmlNodePtr      node, newnode;
      xmlNsPtr        nsptr = NULL;
      xmlChar        *localname, *prefix = NULL;
+     const xmlChar  *retprefix = NULL;

      if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s!",
              &qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) {
@@ -1727,8 +1728,11 @@ PHP_METHOD(SimpleXMLElement, addChild)
              }
      }

-     node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname,
-             newnode->ns ? newnode->ns->prefix : NULL, 1);
+     if ((prefix != NULL || nsuri != NULL) && newnode->ns != NULL) {
+             retprefix = newnode->ns->prefix;
+     }
+
+     node_as_zval_str(sxe, newnode, return_value, SXE_ITER_NONE, localname, retprefix, 1);

will need to change test expectations.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adopted, pushed as 38a7cdc.

Two notes. addChild('kid') under a prefixed parent doesn't read today either: a NULL iter.nsprefix isn't "no filter", match_ns() with a NULL name matches only nodes with no namespace or a null prefix. Your gate keeps that broken, which is fine for 8.4.

Mine broke attribute reads rather than child reads: addAttribute() uses a NULL nsptr, so any prefix filter hides them. Your gate still flips addChild('kid', null, $uri) + addAttribute() from readable to not; pinned in the test.

addChild() passed the caller's prefix to node_as_zval_str() with isprefix
set to 0, so the returned element filtered its children by comparing that
prefix against the namespace href and matched nothing. Take the prefix from
the created node and mark it as one, but only when the caller asked for a
namespace, so a plain addChild() keeps the unfiltered view that its
attributes and non-namespaced children rely on.

Closes phpGH-23599
@iliaal
iliaal force-pushed the promote/simplexml-addchild-ns branch from 10af2d1 to 38a7cdc Compare September 7, 2026 23:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants